// This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
//
//   Keepass2Android is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   Keepass2Android is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using AndroidX.Preference;
using KeePass.Util.Spr;
using keepass2android.services.AutofillBase;
using keepass2android.services.AutofillBase.model;
using Keepass2android.Pluginsdk;
using keepass2android;
using KeePassLib;
using KeePassLib.Utility;
using Kp2aAutofillParser;

namespace keepass2android.services.Kp2aAutofill
{
  [Activity(Label = "@string/app_name",
      ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
      Theme = "@style/Kp2aTheme_ActionBar",
      WindowSoftInputMode = SoftInput.AdjustResize,
      Permission = "keepass2android." + AppNames.PackagePart + ".permission.Kp2aChooseAutofill")]
  public class ChooseForAutofillActivity : ChooseForAutofillActivityBase
  {
    public bool ActivateKeyboardWhenTotpPreference
    {
      get
      {
        return PreferenceManager.GetDefaultSharedPreferences(this)
            .GetBoolean("AutoFillTotp_prefs_ActivateKeyboard_key", false);
      }
    }
    public bool CopyTotpToClipboardPreference
    {
      get
      {
        return PreferenceManager.GetDefaultSharedPreferences(this)
            .GetBoolean("AutoFillTotp_prefs_CopyTotpToClipboard_key", true);
      }
    }

    public bool ShowNotificationPreference
    {
      get
      {
        return PreferenceManager.GetDefaultSharedPreferences(this)
            .GetBoolean("AutoFillTotp_prefs_ShowNotification_key", true);
      }
    }

    protected override Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery, bool useLastOpenedEntry)
    {
      if (useLastOpenedEntry && (App.Kp2a.LastOpenedEntry?.SearchUrl == requestedUrl))
      {
        return null;
      }
      //launch SelectCurrentDbActivity (which is root of the stack (exception: we're even below!)) with the appropriate task.
      //will return the results later
      Intent i = new Intent(this, typeof(SelectCurrentDbActivity));
      //don't show user notifications when an entry is opened.
      var task = new SearchUrlTask()
      {
        UrlToSearchFor = requestedUrl,
        AutoReturnFromQuery = autoReturnFromQuery
      };
      SetTotpDependantActionsOnTask(task);

      task.ToIntent(i);
      return i;
    }

    private void SetTotpDependantActionsOnTask(SelectEntryTask task)
    {
      task.ShowUserNotifications =
          ShowNotificationPreference ? ActivationCondition.WhenTotp : ActivationCondition.Never;
      task.CopyTotpToClipboard = CopyTotpToClipboardPreference;
      task.ActivateKeyboard = ActivateKeyboardWhenTotpPreference
          ? ActivationCondition.WhenTotp
          : ActivationCondition.Never;
    }

    protected override Intent GetOpenEntryIntent(string entryUuid)
    {
      Intent i = new Intent(this, typeof(SelectCurrentDbActivity));
      //don't show user notifications when an entry is opened.
      var task = new OpenSpecificEntryTask() { EntryUuid = entryUuid };
      SetTotpDependantActionsOnTask(task);
      task.ToIntent(i);
      return i;
    }

    protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete;

    protected override FilledAutofillFieldCollection<ViewNodeInputField> GetDataset()
    {
      if (App.Kp2a.CurrentDb == null || (App.Kp2a.QuickLocked))
        return null;
      var entryOutput = App.Kp2a.LastOpenedEntry;

      return GetFilledAutofillFieldCollectionFromEntry(entryOutput, this);
    }

    public static FilledAutofillFieldCollection<ViewNodeInputField> GetFilledAutofillFieldCollectionFromEntry(PwEntryOutput pwEntryOutput, Context context)
    {
      if (pwEntryOutput == null)
        return null;
      FilledAutofillFieldCollection<ViewNodeInputField> fieldCollection = new FilledAutofillFieldCollection<ViewNodeInputField>();
      var pwEntry = pwEntryOutput.Entry;

      foreach (string key in pwEntryOutput.OutputStrings.GetKeys())
      {

        FilledAutofillField field =
            new FilledAutofillField
            {
              AutofillHints = GetCanonicalHintsFromKp2aField(key).ToArray(),
              TextValue = pwEntryOutput.OutputStrings.ReadSafe(key)
            };
        fieldCollection.Add(field);

      }
      if (IsCreditCard(pwEntry, context) && pwEntry.Expires)
      {
        DateTime expTime = pwEntry.ExpiryTime;
        FilledAutofillField field =
            new FilledAutofillField
            {
              AutofillHints = new[] { View.AutofillHintCreditCardExpirationDate },
              DateValue = (long)(1000 * TimeUtil.SerializeUnix(expTime))
            };
        fieldCollection.Add(field);

        field =
            new FilledAutofillField
            {
              AutofillHints = new[] { View.AutofillHintCreditCardExpirationDay },
              TextValue = expTime.Day.ToString()
            };
        fieldCollection.Add(field);

        field =
            new FilledAutofillField
            {
              AutofillHints = new[] { View.AutofillHintCreditCardExpirationMonth },
              TextValue = expTime.Month.ToString()
            };
        fieldCollection.Add(field);

        field =
            new FilledAutofillField
            {
              AutofillHints = new[] { View.AutofillHintCreditCardExpirationYear },
              TextValue = expTime.Year.ToString()
            };
        fieldCollection.Add(field);
      }


      fieldCollection.DatasetName = pwEntry.Strings.ReadSafe(PwDefs.TitleField);
      fieldCollection.DatasetName = SprEngine.Compile(fieldCollection.DatasetName, new SprContext(pwEntry, App.Kp2a.CurrentDb.KpDatabase, SprCompileFlags.All));

      return fieldCollection;
    }

    private static bool IsCreditCard(PwEntry pwEntry, Context context)
    {
      return pwEntry.Strings.Exists("cc-number")
          || pwEntry.Strings.Exists("cc-csc")
          || pwEntry.Strings.Exists(context.GetString(Resource.String.TemplateField_CreditCard_CVV));
    }

    private static readonly Dictionary<string, List<string>> keyToHint = BuildKeyToHints();

    public static string GetKp2aKeyFromHint(string canonicalHint)
    {
      var key = keyToHint.FirstOrDefault(p => p.Value.Contains(canonicalHint)).Key;
      if (string.IsNullOrWhiteSpace(key))
        return canonicalHint;
      return key;
    }

    private static Dictionary<string, List<string>> BuildKeyToHints()
    {
      var result = new Dictionary<string, List<string>>
            {
                {PwDefs.UserNameField, new List<string>{View.AutofillHintUsername, View.AutofillHintEmailAddress}},
                {PwDefs.PasswordField, new List<string>{View.AutofillHintPassword}},
                {PwDefs.UrlField, new List<string>{W3cHints.URL}},
                {
                    LocaleManager.LocalizedAppContext.GetString(Resource.String.TemplateField_CreditCard_CVV),
                    new List<string>{View.AutofillHintCreditCardSecurityCode}
                },
                {
                    LocaleManager.LocalizedAppContext.GetString(Resource.String.TemplateField_CreditCard_Owner),
                    new List<string>{W3cHints.CC_NAME}
                },
                {LocaleManager.LocalizedAppContext.GetString(Resource.String.TemplateField_Number), new List<string>{View.AutofillHintCreditCardNumber}},
                {LocaleManager.LocalizedAppContext.GetString(Resource.String.TemplateField_IdCard_Name), new List<string>{View.AutofillHintName}},
            };
      return result;
    }

    private static List<string> GetCanonicalHintsFromKp2aField(string key)
    {
      List<string> result = new List<string>() { key };
      List<string> hints;
      if (keyToHint.TryGetValue(key, out hints))
        result = hints;
      for (int i = 0; i < result.Count; i++)
      {
        result[i] = result[i].ToLower();
      }
      return result;
    }

    public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder();

  }
}